/**
 *
 * This class abstracts the current Application from the Game-engine Logic.
 * This class may be used only after its initliaze() method has been called.
 *
 * All members and methods of this class are static.
 *
 * It provides a common set of methods which other classes
 * may use without being aware of the fact whether the current application
 * is a Frame or an Applet.
 *
 *
 * @author Somanath
 * @version 1.1	 (last updated 21/10/02)
 *
 * Class Dependencies: None.
 
 	Class - Application
	Abstracts - applet/application 
	Compile time class Dependencies - None
	Known Dependents - Game, ResourceLoader
	Assumptions - assumes AWT applet/application only not swing. | Assumes Applet is passed to it for initialization 
	Purpose - 1. Localize/Abstract Application specific logic in gmengine to single logical place.
			  2. Keep rest of game engine code application/applet agnostic.
			  3. For easier porting of GP to other platform if needed
	Other Info - 
				The Application class runs the application as a 'Applet'. This Applet will have its 'Peer'. The Peer could be either a 'Frame' or the applet itself. Currently there is no support for the application to run as JApplet or have a JFrame as its Peer. Apart from general support for listening to mouse/keyboard event, Application class provides method to createImages() and getGraphics() which help in creating backbuffer and front buffer required for animation by other classes. See Eg of how to use Game.init() method by non-game and game applications.
 
 
*/
package gamepack;

import java.io.InputStream;
import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Frame;
import java.awt.Container;
import java.awt.BorderLayout;
import java.awt.Insets;
import java.net.URL;

import java.awt.LayoutManager;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Application {

	public static final int UNKNOWN=0,APPLET=1,FRAME=2;

	public static int type = UNKNOWN;
	public static boolean isInitialized = false;

	/**
	An Array reflecting the current state of KeyBoard     <br>
	Typical Usage:										  <br>
	if( Application.key[KeyEvent.VK_CONTROL] ) 			  <br>
	{ It means the CONTROL key is currently down.}			*/
	public static boolean key[];


	/** The container for the game. */
	/* This will always point to an applet (stand Alone/within a frame) */
	public static Container c;
	public static ClassLoader classLoader;

	public static Frame f;
	public static Applet a;
	private static KeyListener keyListener;

	static private boolean abortOnLoadError = true;

	/* The function needs to be called once to initialize game. If called more than once, subsequent calls are ignored. 
		If peer is null, then parameter w,h are ignored and the application is assumed to be running as an applet.
		If peer is specified then the application is assumed to be a frame containing the applet of size wxh.
		In this case, this method creates a applet of size wxh and adds it to the frame. 
		Pseudo code would look like this - 
			applet.setSize(w,h);
			peer.add(applet);		
	*/
	
	public static void initialize(int w,int h,Applet applet,Frame peer, LayoutManager layout) {
		if( isInitialized ) return;				
		if( peer == null ) {
			initializeApplet(applet);
		} else {
			initializeFrame(w,h,applet,peer,layout);
		}
	}
	
	/**
		Creates a Framelet using Applet as an argument.
	 	This constructor must be used if the application is truely
	 	an applet and is running within a browser.
	*/
	public static void initializeApplet(Applet applet) {
		if( isInitialized ) return;
		if( applet == null ) throw new RuntimeException(" new Framelet(null) ");
		type=APPLET;	f=null;
		c = a = applet;
		trackKeyBoard();
		applet.requestFocusInWindow();
		isInitialized = true;
	}

	/**
		Creates a Framelet using Applet as a argument.
		This constructor must be used when the current application is not
		running within a browser. This constructor creates a Frame object
		and embeds the applet within that Frame.

		@param w Width of the Frame to be created.
		@param h Height of the Frame to be created.
		@param applet Applet object to be added to the Frame
	*/
	public static void initializeFrame(int w,int h,Applet applet,Frame peer_frame,LayoutManager layout) {
		if( isInitialized ) return;
		type = FRAME;
		classLoader = ClassLoader.getSystemClassLoader();
		c = a = applet;
		applet.setSize(w,h);
		
		if( peer_frame == null )  // defensive check, should never happen.
			throw new RuntimeException("peer_frame is null");
			
		f=peer_frame;
		f.addWindowListener( new WindowAdapter() {
			public void windowClosing(WindowEvent we) { System.exit(0); }  });
		f.setLayout( layout );

		// Code improved using Insets/peer.
		f.addNotify();	// Create f's peer, so that f.getInset() works correctly
		Insets inset = f.getInsets(); //new Insets(24,5,5,5);
		applet.setLocation(inset.left,inset.top);
		f.setSize(w+inset.left+inset.right,h+inset.top+inset.bottom);
		f.add(applet); 
		

		// At this line will the init() & start() method of the applet
		// get invoked ? Will they ever get invoked automatically.
		// While i was working only the paint() method got invoked sometimes.
		f.setVisible(true);

		trackKeyBoard();
		applet.requestFocusInWindow();
		isInitialized = true;
	}
	
	public static void resize( int w, int h ) {
		a.setSize(w,h);
		if( type == FRAME ) {
			f.addNotify();	// Create f's peer, so that f.getInset() works correctly
			Insets inset = f.getInsets(); //new Insets(24,5,5,5);
			a.setLocation(inset.left,inset.top);
			f.setSize(w+inset.left+inset.right,h+inset.top+inset.bottom);					
		} 
	}
	
	public static void maximize() {
	
	}

	/**
		This methods is used to add a WindowListener to the framelet object.
		If the the underlying application is a real applet running in a
		browser	then this method returns without doing anything.
	*/
	public static void addWindowListener(WindowListener wl)
	{	if( type == FRAME )	f.addWindowListener(wl);	}

	public static void removeWindowListener(WindowListener wl)
	{	if( type == FRAME )	f.removeWindowListener(wl);	}

	public static void addKeyListener( KeyListener k ) { c.addKeyListener(k);  }
	public static void removeKeyListener( KeyListener k ) { c.removeKeyListener(k);  }
	public static void setKeyListener( KeyListener k ) { keyListener = k; 	   }

	public static void addMouseListener(MouseListener m) { c.addMouseListener(m);	}
	public static void addMouseMotionListener(MouseMotionListener m) { c.addMouseMotionListener(m);	}
	public static void removeMouseListener(MouseListener m) { c.removeMouseListener(m);	}
	public static void removeMouseMotionListener(MouseMotionListener m) { c.removeMouseMotionListener(m);	}

	public static Graphics getGraphics() { return c.getGraphics(); }
	public static Dimension getSize() {  return c.getSize();   }
	public static void setBackground(Color color) { c.setBackground(color); }
	public static Image createImage(int w,int h) { return c.createImage(w,h); }
	public static Image createImage(int w,int h,Color c) {
		Image img = Application.c.createImage(w,h);
		Graphics g = img.getGraphics();
		g.setColor(c);
		g.fillRect(0,0,w,h);
		return img;
	}

	/** @incomplete
	 	If the Framelet is a true Applet then the Image is loaded
	 	from its CodeBase,otherwise its loaded from the current
	 	working directory. This method throws an exception if it is
	 	unable to load the image.
	 */
	 /* Find out weather getImage returns null or throws an exception.
	 	Make this method throw an exception on failure.


	 */
	public static Image getImage(String s) {
		Image img = null;
		if( type == APPLET ) {
			img = a.getImage(a.getCodeBase(),s);
		}
		else {
			URL url = classLoader.getResource(s);
			if( url == null )
				throw new RuntimeException("classloader.getResource(" + s + ") == null" );
			img = getImage(url);
		}
		return img;
	}
	
	public static InputStream getResourceAsStream(String s) {
		if( type == APPLET ) {
			// God help us, if this does not work with applet due to security exceptions.
			classLoader = ClassLoader.getSystemClassLoader();	// If it isnt initialized..			
		}
		return classLoader.getResourceAsStream(s);		
	}

	public static Image getImage(URL url) {
		
		if( type == APPLET ) return a.getImage(url);
		else 	   			 return Toolkit.getDefaultToolkit().getImage(url);
	}

	/**
	 	This method works only if the Framelet is a true applet.
	 	It shows the the given String in the status bar of the browser.
	 */
	public static void showStatus(String msg) {
		if( type == APPLET ) a.showStatus(msg);
	}

	/**
		This method displays a dialog box with an error message.
		Then it terminates the program. If more than one thread
		call this method, only the first call will be successfull.
	*/
	static public void quit(String errorMessage,String title) {
			/* Code Under Development
			-------------------------
			Note: This code must be synchronized

			Dialog d = new Dialog(f);
			Label l = new Label(errorMessage);
			Button b = new Button("OK");
			b.addActionListener(new QuitHandler());

			d.setSize(400,400);
			d.setTitle(title);
			d.setLayout(new FlowLayout());
			d.add(l);
			d.add(b);

			c.add(d);
			c.setVisible(true);*/
			System.exit(0);
	}


	static public void quit(String errorMessage) {
		quit(errorMessage,"Error");
	}

	// Sees that the "key" field always stays updated */
	private static void trackKeyBoard() {
			key = new boolean[255];
			Application.addKeyListener( new KeyAdapter() {
				public void keyPressed(KeyEvent e) {
					int ch = e.getKeyCode();
					if( ch < 255 )
						key[ch] = true;
					if( keyListener != null )
						keyListener.keyPressed(e);

				}
				public void keyReleased(KeyEvent e) {
					int ch = e.getKeyCode();
					if( ch < 255)
						key[ch] = false;
					if( keyListener != null )
						keyListener.keyReleased(e);

				}
				public void keyTyped(KeyEvent e) {
					if( keyListener != null )
						keyListener.keyTyped(e);
				}
			});
	}


}

